home *** CD-ROM | disk | FTP | other *** search
/ The Programmer Disk / The Programmer Disk (Microforum).iso / xpro / c3 / pro24 / cmdline.c < prev    next >
C/C++ Source or Header  |  1986-08-06  |  11KB  |  342 lines

  1. /* cmdline.c -- command line parsing routines */
  2. /*
  3.  * This module is designed to allow various modules to scan (and rescan)
  4.  * the command line for applicable arguments.  The goal is to hide as
  5.  * much information about switches and their names as possible so that
  6.  * switches become more consistent across applications and so that the
  7.  * author of an application need not do a lot of work to provide numerous
  8.  * options.  Instead, each module scans the command line for its own
  9.  * arguments.
  10.  *
  11.  * Command lines are of the following form:
  12.  *    command -s1 -s2 opt2 -s3 arg1 arg2 -s4 opt4 arg3
  13.  * Note that there are three kinds of command line parameters:
  14.  * (1) A Switch is a "-" followed by a name, e.g. "-s1"
  15.  * (2) An Option is a Switch followed by a space and name, e.g. "-s2 opt2"
  16.  * (3) An Argument is a name by itself, e.g. "arg1"
  17.  * Note also that a switch followed by an argument looks just like an
  18.  * option, so a list of valid option names is necessary to disambiguate.
  19.  *
  20.  * A main program that uses cmdline.c should do the following:
  21.  *    (1) create an array of pointers to strings (char *names[]) that
  22.  *        contains every possible option name
  23.  *    (2) create another array of pointers to strings that contains
  24.  *        every possible switch name
  25.  *    (2) call cl_init(switches, nsw, options, nopt, argv, argc)
  26.  * cl_init will report an error (to stderr) if it finds any illegal
  27.  * switch or option names.
  28.  *
  29.  * Afterward, switches, options, and arguments can be accessed by
  30.  * calling cl_switch, cl_option, and cl_arg.  If cl_switch or cl_option
  31.  * is called with a switch name that was not mentioned in the call to 
  32.  * cl_init, an error will result.  This indicates that the application
  33.  * author omitted a valid switch or option name when calling cl_init.
  34.  * This is an error because the full set of names is needed for error
  35.  * checking and to distinguish arguments from options.
  36.  *
  37.  * cl_nswitch and cl_noption are similar to cl_switch and cl_option,
  38.  * except they each take a list of equivalent switch or option names.  
  39.  * This makes it simple to allow both verbose (-debug) and terse (-d) names.
  40.  */
  41.  
  42. /*****************************************************************************
  43. *        Change Log
  44. *  Date        | Change
  45. *-----------+-----------------------------------------------------------------
  46. * 13-Jun-86 | Created Change Log
  47. *  6-Aug-86 | Modified for Lattice 3.0 -- use "void" to type some routines
  48. *****************************************************************************/
  49.  
  50. #include "cext.h"
  51. #include "stdio.h"
  52. #include "cmdline.h"
  53.  
  54. private char **voptions;    /* valid options */
  55. private int noptions;        /* number of options */
  56. private char **vswitches;    /* valid switches */
  57. private int nswitches;        /* number of switches */
  58. private char **argv;        /* command line argument vector */
  59. private int argc;        /* length of argv */
  60.  
  61. private int cl_rdy = false;    /* set to true when initialized */
  62.  
  63. /*****************************************************************************
  64. *    Routines local to this module
  65. *****************************************************************************/
  66. private    void    check_names();
  67. private int    find_match();
  68. private int    find_string();
  69. private    void    ready_check();
  70.  
  71. /****************************************************************
  72. *            check_names
  73. * Inputs:
  74. *    char *names[]:    array of alternative switch or option names
  75. *    int nnames:    number of alternative switch or option names
  76. *    char *valid[]:    array of valid names
  77. *    int nvalid:    number of valid names
  78. * Effect:
  79. *    Checks that all names are in validnames.  If not, print
  80. *    an error message.
  81. *****************************************************************/
  82.  
  83. private void check_names(names, nnames, valid, nvalid)
  84.     char *names[];
  85.     int nnames;
  86.     char *valid[];
  87.     int nvalid;
  88. {
  89.     int i;    /* loop counters */
  90.     for (i = 0; i < nnames; i++) {
  91.     if (find_string(names[i], valid, nvalid) >= nvalid) {
  92.         fprintf(stderr, "internal error detected by cmdline module:\n");
  93.         fprintf(stderr, "\t'%s' should be in valid lists\n", names[i]);
  94.     }
  95.     }
  96. }
  97.  
  98. /****************************************************************
  99. *            cl_arg
  100. * Inputs:
  101. *    n: the index of the arg needed
  102. * Results:
  103. *    pointer to the nth arg, or NULL if none exists
  104. *    arg 0 is the command name
  105. *****************************************************************/
  106.  
  107. char *cl_arg(n)
  108.     int n;
  109. {
  110.     int i = 1;
  111.     if (n <= 0) return argv[0];
  112.     while (i < argc) {
  113.     if (*argv[i] == '-') {
  114.         if (find_string(argv[i], voptions, noptions) < noptions)
  115.         i += 2; /* skip name and option */
  116.         else i += 1; /* skip over switch name */
  117.     } else if (n == 1) {
  118.         return argv[i];
  119.     } else { /* skip over argument */
  120.         n--;
  121.         i++;
  122.     }
  123.     }
  124.     return NULL;
  125. }
  126.  
  127. /*****************************************************************************
  128. *            cl_init
  129. * Inputs:
  130. *    char *switches[]:    array of switch names
  131. *    int nsw:        number of switch names
  132. *    char *options[]:    array of option names
  133. *    int nopt:        number of option names
  134. *    char *av:        array of command line fields (argv)
  135. *    int ac:            number of command line fields (argc)
  136. * Effect:
  137. *    Checks that all command line entries are valid.
  138. *    Saves info for use by other routines.
  139. * Returns:
  140. *    True if syntax checks OK, otherwise false
  141. *****************************************************************************/
  142.  
  143. boolean cl_init(switches, nsw, options, nopt, av, ac)
  144.     char *switches[];
  145.     int nsw;
  146.     char *options[];
  147.     int nopt;
  148.     char *av[];
  149.     int ac;
  150. {
  151.     int i;    /* index into argv */
  152.     boolean result = true;
  153.  
  154.     vswitches = switches;    nswitches = nsw;
  155.     voptions = options;        noptions = nopt;
  156.     argv = av;            argc = ac;
  157.  
  158.     for (i = 1; i < argc; i++) {  /* case fold lower */
  159.     int j;
  160.     for (j = 0; j < strlen(argv[i]); j++)
  161.         argv[i][j] = tolower(argv[i][j]);
  162.     }
  163.  
  164.     /* check command line syntax: */
  165.     i = 1;
  166.     while (i < argc) {
  167.     if (*argv[i] == '-') {
  168.         if (find_string(argv[i], voptions, noptions) < noptions) {
  169.         i += 1; /* skip name and option */
  170.         if (i < argc && *argv[i] == '-') {
  171.             fprintf(stderr, "missing argument after %s\n", argv[i-1]);
  172.             result = false;
  173.             i += 1;
  174.         }
  175.         } else if (find_string(argv[i], vswitches, nswitches) < 
  176.                nswitches) {
  177.         i += 1; /* skip over switch name */
  178.         } else {
  179.         fprintf(stderr, "invalid switch: %s\n", argv[i]);
  180.         i += 1;
  181.         result = false;
  182.         }
  183.     } else i++; /* skip over argument */
  184.     }
  185.     cl_rdy = true;
  186.     return result;
  187. }
  188.  
  189. /****************************************************************
  190. *            cl_noption
  191. * Inputs:
  192. *    char *names[]:    array of alternative switch names
  193. *    int nnames:    number of alternative switch names
  194. * Result:
  195. *    returns pointer to  if one exists, otherwise null
  196. * Effect:
  197. *    looks for pattern in command line of the form "-n s",
  198. *    where n is a member of names.  Returns pointer to s.
  199. * Implementation:
  200. *    find the option name, then
  201. *    see if the switch is followed by a string that does
  202. *    not start with "-"
  203. *****************************************************************/
  204.  
  205. char *cl_noption(names, nnames)
  206.     char *names[];
  207.     int nnames;
  208. {
  209.     int i;    /* index of switch */
  210.  
  211.     ready_check();
  212.     check_names(names, nnames, voptions, noptions);
  213.     i = find_match(names, nnames) + 1; /* point at the option */
  214.     if (i < argc) { /* make sure option exists */
  215.     if (*(argv[i]) != '-') return argv[i];
  216.     }
  217.     return NULL;
  218. }
  219.  
  220. /*****************************************************************
  221. *            cl_nswitch
  222. * Inputs:
  223. *    char *names[]:    array of alternative switch names
  224. *    int nnames:    number of alternative switch names
  225. * Effect:
  226. *    Checks that names is valid.
  227. *    Finds a pattern in command line of the form "-n", where
  228. *    n is a member of names.
  229. * Result:
  230. *    returns pointer to command line switch if one exists,
  231. *    otherwise null
  232. *****************************************************************/
  233.  
  234. char *cl_nswitch(names, nnames)
  235.     char *names[];
  236.     int nnames;
  237. {
  238.     int i;    /* index of switch */
  239.  
  240.     ready_check();
  241.     check_names(names, nnames, vswitches, nswitches);
  242.     i = find_match(names, nnames);
  243.     if (i < argc) return argv[i];
  244.     /* else */ return NULL;
  245. }
  246.  
  247. /****************************************************************
  248. *            cl_option
  249. * Inputs:
  250. *    char *name:    option name
  251. * Outputs:
  252. *    returns char *: the option string if found, otherwise null
  253. ****************************************************************/
  254.  
  255. char *cl_option(name)
  256.     char *name;
  257. {
  258.     char *names[1];    /* array to hold name */
  259.  
  260.     names[0] = name;
  261.     return cl_noption(names, 1);
  262. }
  263.  
  264. /****************************************************************
  265. *            cl_switch
  266. * Inputs:
  267. *    char *name:    switch name
  268. * Outputs:
  269. *    boolean:    true if switch found
  270. ****************************************************************/
  271.  
  272. boolean cl_switch(name)
  273.     char *name;
  274. {
  275.     char *names[1];    /* array to hold name */
  276.  
  277.     names[0] = name;
  278.     return cl_nswitch(names, 1) != NULL;
  279. }
  280.  
  281. /****************************************************************
  282. *            find_match
  283. * Inputs:
  284. *    char *names[]:    array of alternative switch or option names
  285. *    int nnames:    number of alternative switch or option names
  286. * Effect:
  287. *    Looks for command line switch that matches one of names.
  288. * Returns:
  289. *    Index of switch if found, argc if not found.
  290. *****************************************************************/
  291.  
  292. private int find_match(names, nnames)
  293.     char *names[];
  294.     int nnames;
  295. {
  296.     int j;    /* loop counter */
  297.     for (j = 0; j < argc; j++) {
  298.     if (find_string(argv[j], names, nnames) < nnames) return j;
  299.     }
  300.     return argc;
  301. }
  302.  
  303. /****************************************************************
  304. *            find_string
  305. * Inputs:
  306. *    char *s:    string to find
  307. *    char *names[]:    array of strings
  308. *    int nnames:    number of strings
  309. * Effect:
  310. *    Looks for s in names
  311. * Returns:
  312. *    Index of s in names if found, nnames if not found
  313. *****************************************************************/
  314.  
  315. private int find_string(s, names, nnames)
  316.     char *s;
  317.     char *names[];
  318.     int nnames;
  319. {
  320.     int i; /* loop counter */
  321.     for (i = 0; i < nnames; i++) {
  322.     if (strcmp(s, names[i]) == 0) {
  323.         return i;
  324.     }
  325.     }
  326.     return nnames;
  327. }
  328.  
  329. /****************************************************************
  330. *            ready_check
  331. * Effect:
  332. *    Halt program if cl_rdy is not true.
  333. *****************************************************************/
  334. private void ready_check()
  335. {
  336.     if (!cl_rdy) {
  337.     fprintf(stderr,
  338.         "Internal error: cl_init was not called, see cmdline.c\n");
  339.     exit(1);
  340.     }
  341. }
  342.